/**
 * 
 */
package org.easyframework.support.auditing.annotation.config;

import org.easyframework.support.auditing.annotation.AnnotationAuditAttributeSource;
import org.easyframework.support.auditing.interceptor.AuditInterceptor;
import org.easyframework.support.auditing.interceptor.BeanFactoryAuditAttributeSourceAdvisor;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

/**
 * Parser for the 'auditing' element of the 'laundry' namespace.
 * 
 * @author Amila Domingo
 * @since 1.0
 *
 */
public class AuditingBeanDefinitionParser implements
		BeanDefinitionParser {

	/**
	 * The bean name of the internally managed loggable advisor.
	 */
	public static final String AUDITING_ADVISOR_BEAN_NAME = "org.easyframework.support.auditing.annotation.config..internalAuditAdvisor";
	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
	 */
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);

		return null;
	}
	
	/**
	 * Inner class to just introduce an AOP framework dependency.
	 */
	private static class AopAutoProxyConfigurer {

		private static final String ADVICE_BEAN_NAME = "adviceBeanName";
		private static final String ORDER = "order";
		private static final String LOG_ATTRIBUTE_SOURCE = "logAttributeSource";

		public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
			AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);

			if (!parserContext.getRegistry().containsBeanDefinition(AUDITING_ADVISOR_BEAN_NAME)) {
				Object eleSource = parserContext.extractSource(element);

				// Create the AnnotationLogAttributeSource definition.
				RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationAuditAttributeSource.class);
				sourceDef.setSource(eleSource);
				sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

				// Create the LogInterceptor definition.
				RootBeanDefinition interceptorDef = new RootBeanDefinition(AuditInterceptor.class);
				interceptorDef.setSource(eleSource);
				interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				interceptorDef.getPropertyValues().add(LOG_ATTRIBUTE_SOURCE, new RuntimeBeanReference(sourceName));
				String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

				// Create the BeanFactoryLogAttributeSourceAdvisor definition.
				RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryAuditAttributeSourceAdvisor.class);
				advisorDef.setSource(eleSource);
				advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				advisorDef.getPropertyValues().add(LOG_ATTRIBUTE_SOURCE, new RuntimeBeanReference(sourceName));
				advisorDef.getPropertyValues().add(ADVICE_BEAN_NAME, interceptorName);
				if (element.hasAttribute(ORDER)) {
					advisorDef.getPropertyValues().add(ORDER, element.getAttribute(ORDER));
				}
				parserContext.getRegistry().registerBeanDefinition(AUDITING_ADVISOR_BEAN_NAME, advisorDef);

				CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
						eleSource);
				compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
				compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
				compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, AUDITING_ADVISOR_BEAN_NAME));
				parserContext.registerComponent(compositeDef);
			}
		}
	}
}
